﻿using MLMBonus.Engine;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;

namespace UnitTests.MLMBonus.Engine
{
    
    
    /// <summary>
    ///This is a test class for BonusManager and is intended
    ///to contain all BonusManager Unit Tests
    ///</summary>
    [TestClass()]
    public class BonusManagerTest
    {


        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion


        /// <summary>
        ///A test for Nodes
        ///</summary>
        [TestMethod()]
        public void NodesTest()
        {
            BonusManager target = new BonusManager(new Dictionary<string, INode>());
            Dictionary<string, INode> expected = createTestDictionaryNodes(); 
            Dictionary<string, INode> actual;
            target.Nodes = expected;
            actual = target.Nodes;
            Assert.AreEqual(expected, actual);
            testUnlinkedManagerNodes(target);
            linkManagerNodes(target);
            testManagerNodes(target);
        }

        /// <summary>
        ///A test for BonusManager Constructor
        ///</summary>
        [TestMethod()]
        public void BonusManagerDictionaryConstructorTest()
        {
            BonusManager target = new BonusManager(createTestDictionaryNodes());
            testManagerNodes(target);
        }
        /// <summary>
        ///A test for BonusManager Constructor
        ///</summary>
        [TestMethod()]
        public void BonusManagerIEnumerableConstructorTest()
        {
            BonusManager target = new BonusManager(createTestIEnumNodes());
            testManagerNodes(target);
        }
        /// <summary>
        ///A test for Run
        ///</summary>
        [TestMethod()]
        public void RunTest()
        {
            BonusManager target = new BonusManager(createTestIEnumNodes()); 
            testManagerNodes(target);

            string BonusRunType = string.Empty; // TODO: Initialize to an appropriate value
            target.Run(BonusRunType);
            foreach (NodeTest node in target.Nodes.Values)
            {
                Assert.IsInstanceOfType(node, typeof(NodeTest));
                Assert.IsTrue(node.IsPreProcessed);
                Assert.AreEqual(1, node.PreProcessRan);
                Assert.AreEqual(2, node.PromoteRan);
                Assert.AreEqual(3, node.PayRan);
                Assert.AreEqual(4, node.PostProcessRan);
            }

            NodeTest root = target.Nodes["1"] as NodeTest;
            NodeTest directDescendant = target.Nodes["2"] as NodeTest;
            NodeTest indirectDescendant = target.Nodes["5"] as NodeTest;
            Assert.AreEqual(root.PreProcessOrder, 1);
            Assert.AreEqual(directDescendant.PreProcessOrder, 2);
            Assert.AreEqual(indirectDescendant.PreProcessOrder, 3);
            Assert.AreEqual(indirectDescendant.PostProcessOrder, 4);
            Assert.AreEqual(directDescendant.PostProcessOrder, 5);
            Assert.IsTrue(root.PostProcessOrder > directDescendant.PostProcessOrder);
        }
        #region Manager test helpers
        private void testUnlinkedManagerNodes(BonusManager manager)
        {
            INode root = manager.Nodes["1"];
            INode directDescendant = manager.Nodes["2"];
            INode indirectDescendant = manager.Nodes["5"];

            Assert.IsNotNull(root);
            Assert.IsNotNull(directDescendant);
            Assert.IsNotNull(indirectDescendant);

            Assert.AreEqual(root.Children.Count, 0);
            Assert.IsNull(directDescendant.Parent);
            Assert.IsNull(indirectDescendant.Parent);
        }
        private void linkManagerNodes(BonusManager manager)
        {
            Dictionary<string, INode> nodes = manager.Nodes;
            foreach (INode node in nodes.Values)
            {
                if (nodes.ContainsKey(node.ParentId))
                {
                    INode parent = nodes[node.ParentId];
                    parent.Children.AddLast(node);
                    node.Parent = parent;
                }
            }
        }
        private void testManagerNodes(BonusManager manager)
        {
            INode root = manager.Nodes["1"];
            Assert.IsNotNull(root);
            CollectionAssert.AllItemsAreNotNull(root.Children);

            INode directDescendant = manager.Nodes["2"];
            INode indirectDescendant = manager.Nodes["5"];
            Assert.IsTrue(root.Children.Contains(directDescendant));
            Assert.IsFalse(root.Children.Contains(indirectDescendant));
            Assert.AreEqual(root.Children.First.Value, directDescendant);
            Assert.AreEqual(3, root.Children.Count);
            Assert.AreEqual(1, directDescendant.Children.Count);
        }
        #endregion
        #region Node test helpers
        private Dictionary<string, INode> createTestDictionaryNodes()
        {
            Dictionary<string, INode> nodes = new Dictionary<string, INode>();
            foreach (INode node in createTestIEnumNodes())
            {
                nodes.Add(node.Id, node);
            }
            return nodes;
        }
        private IEnumerable<INode> createTestIEnumNodes()
        {
            List<INode> nodes = new List<INode>();
            nodes.Add(createTestNode("1", ""));
            nodes.Add(createTestNode("2", "1"));
            nodes.Add(createTestNode("3", "1"));
            nodes.Add(createTestNode("4", "1"));
            nodes.Add(createTestNode("5", "2"));
            return nodes as IEnumerable<INode>;
        }
        private INode createTestNode(string id, string parentId)
        {
            NodeBase node = new NodeTest();
            node.Id = id;
            node.ParentId = parentId;
            return node;
        }
        #endregion
    }
    internal class NodeTest : NodeBase
    {
        private static int runOrder;
        public int PreProcessOrder;
        public int PostProcessOrder;
        private int runCounter = 0;
        public int PreProcessRan = 0;
        protected override void RunPreProcess(string BonusRunType)
        {
            PreProcessOrder = ++runOrder;
            PreProcessRan = ++runCounter;
        }

        public int PromoteRan = 0;
        protected override void RunPromote(string BonusRunType)
        {
            PromoteRan = ++runCounter;
        }

        public int PostProcessRan = 0;
        protected override void RunPostProcess(string BonusRunType)
        {
            PostProcessOrder = ++runOrder;
            PostProcessRan = ++runCounter;
        }
        public int PayRan = 0;
        public override void Pay(string BonusRunType)
        {
            base.Pay(BonusRunType);
            PayRan = ++runCounter;
        }
    }
}
